package com.qiandw.mailSendService.module;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
import java.util.Enumeration;
import java.util.List;

public class CertUtil {

    private static final Logger logger = LoggerFactory.getLogger(CertUtil.class);

    /**
     * 获取x509 证书对象
     * @param pfxBytes
     * @param password
     * @return 证书对象
     */
    public static X509Certificate getCertificateByPfx(byte[] pfxBytes, String password) {
        KeyStore objKs = getKeyStoreByPfx(pfxBytes, password);
        String aliasesName = getAliasesNameFinallyByKeyStore(objKs);
        Certificate certificate = null;
        try {
            certificate = objKs.getCertificate(aliasesName);
        } catch (KeyStoreException e) {
            e.printStackTrace();
            logger.error("获取x509证书失败", e);
        }
        return (X509Certificate) certificate;
    }

    private static String getAliasesNameFinallyByKeyStore(KeyStore keyStore){
        Enumeration<String> objEnum = null;
        String aliasesName = "";
        try {
            objEnum = keyStore.aliases();
            while (objEnum.hasMoreElements()){
                aliasesName = objEnum.nextElement();
            }
        } catch (KeyStoreException e) {
            logger.error("keyStore获取alias失败", e);
        }
        return aliasesName;
    }

    /**
     * 获取证书链
     * @param keyStore pwd Keys Store
     * @return Certificates chain
     */
    public static Certificate[] getCertificateChainByKeyStore(KeyStore keyStore) throws KeyStoreException {
        String aliasesName = getAliasesNameFinallyByKeyStore(keyStore);
        return keyStore.getCertificateChain(aliasesName);
    }

    /**
     * 获取Keystore
     * @param pfxBytes 文件
     * @param password private people own pk
     * @return pwd keys Store
     * get keyStore {@link KeyStore#getEntry(String, KeyStore.ProtectionParameter)}
     */
    public static KeyStore getKeyStoreByPfx(byte[] pfxBytes, String password){
        KeyStore objKs = null;
        try {
            objKs = KeyStore.getInstance("PKCS12");
            objKs.load(new ByteArrayInputStream(pfxBytes), password.toCharArray()); //加载keystore
        } catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException e) {
            logger.error("获取Keystore错误", e);
        }
        return objKs;
    }

    public static PrivateKey getPrivateKeyByKeyStore(KeyStore objKs, String pwd) {

        String aliasesName = getAliasesNameFinallyByKeyStore(objKs);
        PrivateKey privateKey = null;
        try {
            privateKey = (PrivateKey) objKs.getKey(aliasesName, pwd.toCharArray());
        } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
            logger.error("私钥获取不成功",e);
        }
        return privateKey;
    }

    public static PrivateKey getPrivateKeyByPfx(byte[] pfxBytes, String password) throws KeyStoreException {
        KeyStore objKs = getKeyStoreByPfx(pfxBytes, password);
        return getPrivateKeyByKeyStore(objKs, password);
    }

    private static byte[] cryptOperatorByPrivateKey(Key key, byte[] data, int mode){
        try {
            Cipher cipher = Cipher.getInstance(key.getAlgorithm());
            cipher.init(mode, key);
            return cipher.doFinal(data);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
            logger.error("算法操作获取失败", e);
        }
        return null;
    }

    public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] data){
        return cryptOperatorByPrivateKey(privateKey, data, Cipher.ENCRYPT_MODE);
    }

    public static byte[] decryptByPrivateKey(PrivateKey privateKey, byte[] data){
        return cryptOperatorByPrivateKey(privateKey, data, Cipher.DECRYPT_MODE);
    }

    public static PublicKey getPublicKeyByCert(X509Certificate cert){
        return cert.getPublicKey();
    }

    public static byte[] encryptByPublicKey(PublicKey publicKey, byte[] data){
        return cryptOperatorByPrivateKey(publicKey, data, Cipher.ENCRYPT_MODE);
    }

    public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] data){
        return cryptOperatorByPrivateKey(publicKey, data, Cipher.DECRYPT_MODE);
    }

    public static Signature getSignatureObj(X509Certificate cert){
        try {
            return Signature.getInstance(cert.getSigAlgName());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            logger.error("x509证书的签名对象获取失败",e);
        }
        return null;
    }

    public static byte[] sign(String signAlgName, PrivateKey privateKey, byte[] data){
        try {
            Signature signature = Signature.getInstance(signAlgName);
            signature.initSign(privateKey);
            signature.update(data);
            return signature.sign();
        } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
            e.printStackTrace();
            logger.error("签名失败", e);
        }
        return null;
    }

    public static Boolean verify(X509Certificate cert, byte[] data, byte[] sign){
        boolean isVerify = false;
        try {
            Signature signature = getSignatureObj(cert);
            if (signature == null) return false;
            signature.initVerify(cert);
            signature.update(data);
            isVerify = signature.verify(sign);
        } catch (InvalidKeyException | SignatureException e) {
            e.printStackTrace();
            logger.error("签名认证失败",e);
        }
        return isVerify;
    }

    public static Boolean PKCS7Verify(X509Certificate dn, byte[] data, byte[] signature) {
        boolean isVerify = false;
        try {
            PKCS7 p7 = new PKCS7(signature);
            X509Certificate[] certs = p7.getCertificates();
            SignerInfo[] sis = p7.verify(data); //进行数据信息验证
            List<SignerInfo> sisList = Arrays.asList(sis);
            for (SignerInfo  si : sisList){
                X509Certificate cert = si.getCertificate(p7);
                // 证书是否过期验证，如果不用系统日期可用cert.checkValidity(date);
                //cert.checkValidity();
                byte[] signed = cert.getSignature(); //获取证书签名的信息
                logger.info("签名后数据Base64：" + Base64.getEncoder().encodeToString(signed));
                if (cert.equals(dn)){
                    isVerify = true;
                    break;
                }
            }
        } catch (IOException | NoSuchAlgorithmException | SignatureException e) {
            e.printStackTrace();
        }/* catch (CertificateExpiredException | CertificateNotYetValidException e) {
            logger.info("证书已经过期 -- ERROR PKCS7Verify 200");
        }*/
        return isVerify;
    }

    public static X509Certificate getCertificateByFile(byte[] certFile){
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(certFile));
            return (X509Certificate) certificate;
        } catch (CertificateException e) {
            e.printStackTrace();
            logger.error("x509证书读取失败",e);
        }
        return null;
    }

}
